
#ifndef ROPUFU_WINDOW_RLR_HPP_INCLUDED
#define ROPUFU_WINDOW_RLR_HPP_INCLUDED

#include <array>
#include <cstdint>
#include <ostream>
#include <string>
#include <vector>

namespace ropufu
{
    template <std::size_t t_count_streams>
    struct window_rlr
    {
        static const std::size_t count_streams = t_count_streams;
        typedef window_rlr<count_streams> type;

    private:
        std::size_t m_regen_time = 0UL;
        std::size_t m_time = 0UL;

        // The complete log-LR: (l_{1} + \cdots + l_{t}).
        std::array<double, count_streams> m_complete_loglikelihood;
        // The accumulated log-LR since the latest regeneration time: (l_{\sigma (t) + 1} + \cdots + l_{t}).
        std::array<double, count_streams> m_regen_loglikelihood;

        // The (truncated) history or observations' accumulated log-likelihood ratio since last regeneration time.
        std::array<std::vector<double>, count_streams> m_loglikelihood_histroy;

    public:
        explicit window_rlr()
        {
            this->reset();
        }

        void reset()
        {
            this->m_regen_time = 0UL;
            this->m_time = 0UL;
            this->m_complete_loglikelihood.fill(0.0);
            this->m_regen_loglikelihood.fill(0.0);
            for (std::size_t k = 0; k < count_streams; k++)
            {
                this->m_loglikelihood_histroy[k].clear();
            }
        }

        std::size_t get_depth() const
        {
            return this->m_loglikelihood_histroy[0].size(); // Note: same for all 1 \le k \le count_streams.
        }

        void observe(const std::array<double, count_streams>& loglikelihood)
        {
            bool do_regen = true;
            for (std::size_t k = 0; k < count_streams; k++) this->m_complete_loglikelihood[k] += loglikelihood[k];
            for (std::size_t k = 0; k < count_streams; k++)
            {
                double l = loglikelihood[k];
                std::vector<double>& local_histroy = this->m_loglikelihood_histroy[k]; // Just to make things more readable.
                for (double& u : local_histroy) u += l; // Update the accumulated log-LR history with current observation.

                this->m_regen_loglikelihood[k] += l; // Update (Z_t^k - Z_{\sigma (t)}^k).
                if (this->m_regen_loglikelihood[k] >= 0.0) do_regen = false; // If at least one is non-negative then do not regenerate.

                local_histroy.emplace_back(l);
            }

            if (do_regen)
            {
                // Clear the accumulated log-LR.
                this->m_regen_loglikelihood.fill(0.0);
                this->m_regen_time = this->m_time + 1;
                //std::cout << "Regen time at " << this->m_regen_time << "." << std::endl;

                // Clear the history.
                for (std::size_t k = 0; k < count_streams; k++)
                {
                    this->m_loglikelihood_histroy[k].clear();
                }
            }

            this->m_time++;
        }

        // Returns the complete loglikelihood ratio.
        const std::array<double, count_streams>& complete() const
        {
            return this->m_complete_loglikelihood;
        }

        // Returns the accumulated loglikelihood ratio.
        const std::array<double, count_streams>& accumulated() const
        {
            return this->m_regen_loglikelihood;
        }

        // Returns the history of observations since the last reset.
        const std::array<std::vector<double>, count_streams>& get_history() const
        {
            return this->m_loglikelihood_histroy;
        }

        double get_history_at(std::size_t stream_index, std::size_t time_index) const
        {
            return this->m_loglikelihood_histroy[stream_index][time_index];
        }

        friend std::ostream& operator <<(std::ostream& os, const type&)
        {
            return os << "window_rlr";
        }
    };
}

#endif // ROPUFU_WINDOW_RLR_HPP_INCLUDED
